from socket import *
import sys, argparse, os

def argsparse():
    help = "This will do a portscan on an IP / hostname you specify."
    epi = "You can use both --ports and --range, but at least one is required."
    validchoices = []
    for i in range(0, 65536): validchoices.append(i)
    parser = argparse.ArgumentParser(description = help, epilog = epi)

    parser.add_argument("--host", "-H", help = "Hostname for connection", required = True)
    parser.add_argument("--ports", "-P", help = "Single ports to scan", type = int, nargs = "*")
    parser.add_argument("--range", "-R", help = "Range of ports to scan (both values included)",
                        type = int, nargs = 2)
    parser.add_argument("--timeout", help = "Timeout used for connection (default: 0.2)", type = float, default = 0.2)

    args = parser.parse_args()
        
    if not args.ports and not args.range:
        parser.error("You have to specify at least some single ports or a port range.")
    
    try: prange = range(args.range[0], args.range[1] + 1)
    except: prange = None
            
    ports = []
        
    if args.ports:
        for i in args.ports: ports.append(i)
    if prange:
        for i in prange: ports.append(i)
    
    ports.sort()
    
    for i in ports:
        if i < 0 or i > 65535:
            parser.error("You have to choose valid ports (0 - 65535)!")
    
    r = (args.host, ports, args.timeout)
    
    return(r)
    

def scan(ip, port, timeout):
    
    winstatus={
                0:"open",
            10049:"address not available",
            10061:"closed",
            10060:"timeout",
            10056:"already connected",
            10035:"filtered",
            11001:"IP not found",
            10013:"permission denied"
            }
    
    s = socket(AF_INET, SOCK_STREAM)
    s.settimeout(timeout)
    try:
        result = s.connect_ex((ip, port))
    except KeyboardInterrupt:
        sys.exit()
    except:
        sys.stdout.write("Cannot connect to IP!\n")
        sys.stdout.flush()
        return
    s.close()
    
    if sys.platform == "Windows" or os.name == "nt":
        return(winstatus[result])

def scanports(ip, portrange, timeout):
    try:
        for i in portrange:
            sys.stdout.write("Scanning port {}...\r".format(repr(i).rjust(5)))
            sys.stdout.flush()
            res = scan(ip, i, timeout)
            if not res == "filtered" and not res == "closed":
                sys.stdout.write("Found port: {} [{}]\n".format(repr(i).rjust(5), res))
                sys.stdout.flush()
    except KeyboardInterrupt:
        sys.stdout.write("\nPortscan has been interrupted by Keyboard.")
        sys.stdout.flush()
        sys.exit()
        
if __name__ == "__main__":
    args = argsparse()
    scanports(args[0], args[1], args[2])
    sys.stdout.write("\nFinished portscan.")
    sys.stdout.flush()